﻿Public Class Train
    ' Vitesse de train (avancée par mise à jour)
    Private Const Speed As Integer = 3

    ' Nombre de pixels par wagon
    Public Const WagonLength As Integer = 15

    ' Temps d'arrêt sur une station
    Private Const StopTime As Integer = 40

    ' Nombre de wagons
    Private _WagonCount As Integer
    Public Property WagonCount() As Integer
        Get
            Return _WagonCount
        End Get
        Set(ByVal Value As Integer)
            _WagonCount = Value
        End Set
    End Property

    ' Timer d'arrêt
    Private _StopTimer As Integer
    Public Property StopTimer() As Integer
        Get
            Return _StopTimer
        End Get
        Set(ByVal Value As Integer)
            _StopTimer = Value
        End Set
    End Property

    ' Nom du train
    Private _Name As String
    Public Property Name() As String
        Get
            Return _Name
        End Get
        Set(ByVal Value As String)
            _Name = Value
        End Set
    End Property

    ' Départ (index de la station)
    Private _PrevStation As Integer
    Public Property PrevStation() As Integer
        Get
            Return _PrevStation
        End Get
        Set(ByVal Value As Integer)
            _PrevStation = Value
        End Set
    End Property

    ' Destination (index station)
    Private _HeadingTo As Integer
    Public Property HeadingTo() As Integer
        Get
            Return _HeadingTo
        End Get
        Set(ByVal Value As Integer)
            _HeadingTo = Value
        End Set
    End Property

    ' Destination suivante (index station, prévision)
    Private NextStation As Integer

    ' Position du train sur la jonction
    Private _JunctionPosition As Double
    Public Property JunctionPosition() As Double
        Get
            Return _JunctionPosition
        End Get
        Set(ByVal Value As Double)
            _JunctionPosition = Value
        End Set
    End Property

    ' Pour chaque station, on liste les stations de destination
    Private StationsStats As New Dictionary(Of Integer, List(Of Integer))

    ' Parcours
    Public Route As New Route

    ' Constructeur
    Sub New(HeadingTo As Integer, PrevStation As Integer, JunctionPosition As Double, WagonCount As Integer, StopTimer As Integer, Name As String)
        _HeadingTo = HeadingTo
        _PrevStation = PrevStation
        _JunctionPosition = JunctionPosition
        _WagonCount = WagonCount
        _StopTimer = StopTimer
        _Name = Name
    End Sub

    ' Détermination de la prochaine station à visiter
    Private Function FindNextStation(FromS As Integer, ToS As Integer, ByRef Junctions As List(Of Junction), ByRef RailPoints As List(Of RailPoint)) As Integer
        ' Si le parcours est défini
        If Route.IsActive() Then
            ' On obtient la prochaine station à partir de notre parcours
            Return Route.GetNextStation(FromS, ToS)
        Else
            ' On créé une liste représentant toutes les jonctions possibles après celle en cours
            Dim PossibleStations As List(Of Integer) = GetNeighbors(ToS, Junctions, RailPoints, FromS)

            ' Si on a sélectionné au moins une station possible
            If PossibleStations.Count > 0 Then
                ' Si la liste des stats stations ne contient pas la destination, on peut emprunter celle qu'on veut (ici, la dernière)
                If Not (StationsStats.ContainsKey(_HeadingTo)) Then Return PossibleStations.Last

                ' On va parcourir les stations possibles et stocker la dernière au cas où on ne trouverait pas de station pas encore empruntée
                Dim Last As Integer

                ' Pour chaque station possible
                For Each PossibleStation As Integer In PossibleStations
                    Last = PossibleStation
                    ' On vérifie qu'elle ne se trouve pas dans la liste des stations déjà empruntées, et on la retourne
                    If Not (StationsStats.Item(_HeadingTo).Contains(PossibleStation)) Then Return PossibleStation
                Next

                ' Toutes les jonctions possibles ont été empruntées, on renvoie la dernière
                Return PossibleStations.Last
            End If

            ' Aucune jonction possible, il s'agit d'un terminus => on repart dans l'autre sens
            Return FromS
        End If
    End Function

    ' Validation de la prochaine station
    Public Sub ValidateNextStation(ByRef Junctions As List(Of Junction), ByRef RailPoints As List(Of RailPoint))
        ' On détermine la future station et on l'assigne
        NextStation = FindNextStation(_PrevStation, _HeadingTo, Junctions, RailPoints)
        ' Puis, si le parcours n'est pas défini, on enregistre le passage dans notre liste servant à faire les stats
        If Not Route.IsActive() Then RecordStation(Junctions, RailPoints)
    End Sub

    ' Enregistrement des stations déjà visitées pour ne pas tout le temps passer par la même
    Private Sub RecordStation(ByRef Junctions As List(Of Junction), ByRef RailPoints As List(Of RailPoint))
        ' Si la liste contient déjà la station de destination
        If StationsStats.ContainsKey(_HeadingTo) Then
            ' Si on veut aller vers une station déjà parcourue dans le passé
            If StationsStats.Item(_HeadingTo).Contains(NextStation) Then
                ' On vide la liste des stations parcourues pour cette destination
                StationsStats.Item(_HeadingTo).Clear()
                ' Puis on revalide la station suivante
                ValidateNextStation(Junctions, RailPoints)
            Else
                ' La future station est marquée comme empruntée
                StationsStats.Item(_HeadingTo).Add(NextStation)
            End If
        Else
            ' Si la destination n'est pas présente dans la liste, il faut créer une liste des futures stations pour cette station
            StationsStats.Add(_HeadingTo, New List(Of Integer))
            ' Et ajouter la future station
            StationsStats.Item(_HeadingTo).Add(NextStation)
        End If
    End Sub

    ' Mise à jour
    Public Sub Tick(ByRef Junctions As List(Of Junction), ByRef Stations As Dictionary(Of Integer, Station), ByRef RailPoints As List(Of RailPoint))
        ' Distance entre les deux stations de la jonction courante
        Dim Distance As Double = GetDistance(Stations.Item(_PrevStation), Stations.Item(_HeadingTo))

        ' Si le milieu du train se trouve sur la station
        If JunctionPosition > Distance - (_WagonCount * WagonLength / 2) - Speed - 1 Then
            If Route.IsActive Then
                If _StopTimer = -1 And Route.IsStop(_HeadingTo) Then
                    _StopTimer = StopTime
                End If
            End If
        End If

        ' Si la tête du train dépasse la station d'arrivée (corrigé pour être fluide)
        If JunctionPosition > Distance - Speed - 1 Then
            ' Si on est arrivé sur un terminus
            If NextStation = _PrevStation Then
                ' On change le sens du train (qui passera sur l'autre voie)
                Dim tmp As Integer = _HeadingTo
                _HeadingTo = _PrevStation
                _PrevStation = tmp
            Else
                _PrevStation = _HeadingTo
                ' Sinon, la prochaine station sera la station prévue
                _HeadingTo = NextStation
            End If

            ' Puis on actualise l'index de la prochaine jonction
            ValidateNextStation(Junctions, RailPoints)

            ' Sans oublier de partir du début de la jonction, vu qu'on vient d'en changer
            _JunctionPosition = 0

            _StopTimer = -1
        End If

        If _StopTimer < 1 Then
            ' A chaque mise à jour, on fait progresser la position du train sur la jonction courante grâce à la constante vitesse
            _JunctionPosition = _JunctionPosition + Speed
        Else
            _StopTimer = _StopTimer - 1
        End If
    End Sub

    Public Sub Draw(ByRef Gfp As Drawing2D.GraphicsPath, ByRef Junctions As List(Of Junction), _
                    ByRef Stations As Dictionary(Of Integer, Station))
        ' Direction (angle) de la jonction courante
        Dim A As Double = GetAngle(Stations.Item(_PrevStation), Stations.Item(_HeadingTo))

        ' Coordonnées du point de contrôle (point représentant la jonction selon la voie)
        Dim SP As Point = New Point(Stations.Item(_PrevStation).X + Math.Cos(A - Math.PI / 4) * 3, _
                                    Stations.Item(_PrevStation).Y + Math.Sin(A - Math.PI / 4) * 3)

        ' Longueur du train en pixel
        Dim TrainLength As Integer = _WagonCount * WagonLength
        ' Longueur du train sur la voie utilisée pour le dessin
        Dim EndLength As Double = _JunctionPosition + TrainLength

        ' Longueur en pixels de la jonction où se trouve le train
        Dim Distance As Double = GetDistance(Stations.Item(_PrevStation), Stations.Item(_HeadingTo)) - TrainLength

        ' Si on empiète sur la prochaine jonction
        If _JunctionPosition > Distance Then
            ' On détermine l'extrémité du train (arrière)
            Dim P2 As New Point(SP.X + Math.Cos(A) * _JunctionPosition, SP.Y + Math.Sin(A) * _JunctionPosition)

            ' On se prépare à calculer un point de contrôle symétrique au centre de la station
            Dim A1 As Double = GetAngle(Stations.Item(_HeadingTo), Stations.Item(_PrevStation))
            Dim SP1 As Point = New Point(Stations.Item(_HeadingTo).X + Math.Cos(A1 + Math.PI / 4) * 3, _
                                         Stations.Item(_HeadingTo).Y + Math.Sin(A1 + Math.PI / 4) * 3)

            ' On va calculer le point de contrôle représentant la première station de la prochaine jonction
            Dim A2 As Double = GetAngle(Stations.Item(_HeadingTo), Stations.Item(NextStation))
            Dim SP2 As Point = New Point(Stations.Item(_HeadingTo).X + Math.Cos(A2 - Math.PI / 4) * 3, _
                                         Stations.Item(_HeadingTo).Y + Math.Sin(A2 - Math.PI / 4) * 3)

            ' Calcul de l'extrémité du train (avant) sur la prochaine jonction
            Dim P1 As Point = New Point(SP2.X + Math.Cos(A2) * (_JunctionPosition - Distance), _
                                        SP2.Y + Math.Sin(A2) * (_JunctionPosition - Distance))

            ' On dessine l'avant
            Gfp.StartFigure()
            Gfp.AddLine(P1, SP2)
            Gfp.CloseFigure()

            ' On dessine le milieu
            Gfp.StartFigure()
            Gfp.AddLine(SP1, SP2)
            Gfp.CloseFigure()

            ' On dessine l'arrière
            Gfp.StartFigure()
            Gfp.AddLine(SP1, P2)
            Gfp.CloseFigure()
        Else
            ' Si le train n'est pas entre deux stations, calcule juste les deux extrémités du train sur la voie
            Dim P1 As New Point(SP.X + Math.Cos(A) * _JunctionPosition, SP.Y + Math.Sin(A) * _JunctionPosition)
            Dim P2 As New Point(SP.X + Math.Cos(A) * EndLength, SP.Y + Math.Sin(A) * EndLength)

            ' Et on l'ajoute
            Gfp.StartFigure()
            Gfp.AddLine(P1, P2)
            Gfp.CloseFigure()
        End If
    End Sub
End Class
